﻿/*--------------------------------------------------
 Creater：Junxuan.Su
 Time：2013-12-5 20:12:04
 Remark : HtmlEngine
--------------------------------------------------*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;

namespace HXDNB.Infrastructure
{
    public class HtmlEngine : IDisposable
    {
        private XmlDocument _html;

        /// <summary>
        /// 输出字符串
        /// </summary>
        public string OutXml
        {
            get { return _html.OuterXml; }
        }

        public HtmlEngine()
        {
            _html = new XmlDocument();
        }

        #region Creation

        /// <summary>
        /// 加载Html模板
        /// </summary>
        /// <param name="name">模版名称</param>
        /// <returns>是否加载成功</returns>
        public bool Load(string name)
        {
            try
            {
                string path = Config.Template + name;
                if (!File.Exists(path))
                    return false;

                _html.XmlResolver = new HtmlResolver();
                _html.Load(path);

                return true;
            }
            catch (Exception ex)
            {
                Log.Error("HtmlEngine.Load", string.Format("文件名:{0}，{1}", name, ex.Message + ex.StackTrace));
                throw;
            }
        }

        /// <summary>
        /// 处理页面实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="model">实体对象</param>
        public void SetModel<T>(T model)
        {
            foreach (var item in typeof(T).GetProperties())
            {
                var value = item.GetValue(model, null);
                _html.InnerXml = _html.InnerXml.Replace("{$" + item.Name + "}", value == null ? "" : value.ToString());
            }

        }

        /// <summary>
        /// 处理指定区域实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="tagId">标签Id</param>
        /// <param name="model">实体对象</param>
        public void SetModel<T>(string tagId, T model)
        {
            var tag = GetByID(tagId);
            if (tag == null) return;

            foreach (var item in typeof(T).GetProperties())
            {
                var value = item.GetValue(model, null);
                tag.InnerXml = tag.InnerXml.Replace("{$" + item.Name + "}", value == null ? "" : value.ToString());
            }
        }

        /// <summary>
        /// 处理循环结构
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="list">数据源</param>
        public void SetFor<T>(IEnumerable<T> list)
        {
            SetFor("divFor", list);
        }

        /// <summary>
        /// 处理循环结构
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="tagId">标签Id</param>
        /// <param name="list">数据源</param>
        public void SetFor<T>(string tagId, IEnumerable<T> list)
        {
            var tag = GetByID(tagId);
            if (tag == null) return;

            var array = typeof(T).GetProperties();
            var content = new StringBuilder();
            foreach (var item in list)
            {
                var str = tag.InnerXml;
                foreach (var pi in array)
                {
                    var value = pi.GetValue(item, null);
                    str = str.Replace("{$#" + pi.Name + "}", value == null ? "" : value.ToString());
                }

                content.Append(str);
            }

            tag.InnerXml = content.ToString();
        }

        /// <summary>
        /// 设置所有部分视图
        /// </summary>
        public void SetPartials()
        {
            var partials = GetList("*", "path");
            for (var i = 0; i < partials.Count; i++)
                SetPartial(partials.Item(i));
        }

        /// <summary>
        /// 设置部分视图
        /// </summary>
        /// <param name="id">id</param>
        public void SetPartial(string id)
        {
            SetPartial(GetByID(id));
        }

        /// <summary>
        /// 设置部分视图
        /// </summary>
        /// <param name="tag">节点</param>
        public void SetPartial(XmlNode tag)
        {
            if (tag == null) return;
            if (tag.Attributes == null) return;
            var tempName = tag.Attributes["path"].Value;
            if (string.IsNullOrWhiteSpace(tempName)) return;

            var path = Config.Template + tempName.TrimStart('/');
            if (!File.Exists(path)) return;

            tag.InnerXml = File.ReadAllText(path);
        }

        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="path">保存路径</param>
        public bool Save(string path)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(path))
                    return false;

                if (!Directory.Exists(Path.GetDirectoryName(path)))
                    Directory.CreateDirectory(Path.GetDirectoryName(path));

                File.WriteAllText(path, _html.OuterXml, Encoding.UTF8);
                return true;
            }
            catch (Exception ex)
            {
                Log.Error("HtmlEngine.Save", ex.Message + ex.StackTrace);
                throw;
            }
        }

        /// <summary>
        /// 部署文件
        /// </summary>
        /// <param name="relativePath">相对路径</param>
        public bool Deploy(string relativePath)
        {
            try
            {
                SetPartials();
                Save(string.Format("{0}/{1}", Config.Preview.TrimEnd('/'), relativePath.TrimStart('/')));
                Save(string.Format("{0}/{1}", Config.Release.TrimEnd('/'), relativePath.TrimStart('/')));
                return true;
            }
            catch (Exception ex)
            {
                Log.Error("HtmlEngine.Deploy", ex.Message + ex.StackTrace);
                throw;
            }
        }

        #endregion

        #region Path

        /// <summary>
        /// 根据Id查询节点
        /// </summary>
        /// <param name="id">标签Id</param>
        /// <returns>Xml节点</returns>
        public XmlNode GetByID(string id)
        {
            return Search(GetXPath("*", "id", id), null);
        }

        /// <summary>
        ///  根据Id查询节点
        /// </summary>
        /// <param name="id">标签Id</param>
        /// <param name="parentNode">父节点</param>
        /// <returns>Xml节点</returns>
        public XmlNode GetByID(string id, XmlNode parentNode)
        {
            return Search(GetXPath("*", "id", id), parentNode);
        }

        /// <summary>
        /// 根据Name查询节点
        /// </summary>
        /// <param name="name">标签Name</param>
        /// <returns>Xml节点</returns>
        public XmlNode GetByName(string name)
        {
            return Search(GetXPath("*", "name", name), null);
        }

        /// <summary>
        /// 根据Name查询节点
        /// </summary>
        /// <param name="name">标签Name</param>
        /// <param name="parentNode">父节点</param>
        /// <returns>Xml节点</returns>
        public XmlNode GetByName(string name, XmlNode parentNode)
        {
            return Search(GetXPath("*", "name", name), parentNode);
        }

        /// <summary>
        /// 根据属性查询节点
        /// </summary>
        /// <param name="tag">标签</param>
        /// <param name="attr">属性</param>
        /// <param name="value">值</param>
        /// <param name="parentNode">父节点</param>
        /// <returns>Xml节点</returns>
        public XmlNode Get(string tag, string attr, string value, XmlNode parentNode)
        {
            return Search(GetXPath(tag, attr, value), parentNode);
        }

        /// <summary>
        /// 根据标签查询节点集合
        /// </summary>
        /// <param name="tag">标签</param>
        /// <returns>Xml节点集合</returns>
        public XmlNodeList GetList(string tag)
        {
            return Select(GetXPath(tag, null, null), null);
        }

        /// <summary>
        /// 根据标签属性查询节点集合
        /// </summary>
        /// <param name="tag">标签</param>
        /// <param name="attr">属性</param>
        /// <returns>Xml节点集合</returns>
        public XmlNodeList GetList(string tag, string attr)
        {
            return Select(GetXPath(tag, attr, null), null);
        }

        /// <summary>
        /// 根据标签属性值查询节点集合
        /// </summary>
        /// <param name="tag">标签</param>
        /// <param name="attr">属性</param>
        /// <param name="value">值</param>
        /// <returns>Xml节点集合</returns>
        public XmlNodeList GetList(string tag, string attr, string value)
        {
            return Select(GetXPath(tag, attr, value), null);
        }

        /// <summary>
        /// 根据标签查询节点集合
        /// </summary>
        /// <param name="tag">标签</param>
        /// <param name="parentNode">父节点</param>
        /// <returns>Xml节点集合</returns>
        public XmlNodeList GetList(string tag, XmlNode parentNode)
        {
            return Select(GetXPath(tag, null, null), parentNode);
        }

        /// <summary>
        /// 根据标签属性查询节点集合
        /// </summary>
        /// <param name="tag">标签</param>
        /// <param name="attr">属性</param>
        /// <param name="parentNode">父节点</param>
        /// <returns>Xml节点集合</returns>
        public XmlNodeList GetList(string tag, string attr, XmlNode parentNode)
        {
            return Select(GetXPath(tag, attr, null), parentNode);
        }

        /// <summary>
        /// 根据标签属性值查询节点集合
        /// </summary>
        /// <param name="tag">标签</param>
        /// <param name="attr">属性</param>
        /// <param name="value">值</param>
        /// <param name="parentNode">父节点</param>
        /// <returns>Xml节点集合</returns>
        public XmlNodeList GetList(string tag, string attr, string value, XmlNode parentNode)
        {
            return Select(GetXPath(tag, attr, value), parentNode);
        }

        /// <summary>
        /// 查询节点
        /// </summary>
        /// <param name="xPath">位置</param>
        /// <param name="parent">父节点</param>
        /// <returns>Xml节点</returns>
        protected XmlNode Search(string xPath, XmlNode parent)
        {
            try
            {
                return parent != null ? parent.SelectSingleNode(xPath.Replace("//", "descendant::")) : _html.SelectSingleNode(xPath);
            }
            catch (Exception ex)
            {
                Log.Error("HtmlEngine.Search", ex.Message);
                return null;
            }
        }

        /// <summary>
        /// 查询节点集合
        /// </summary>
        /// <param name="xPath">位置</param>
        /// <param name="parent">父节点</param>
        /// <returns>Xml节点集合</returns>
        protected XmlNodeList Select(string xPath, XmlNode parent)
        {
            try
            {
                return parent != null ? parent.SelectNodes(xPath.Replace("//", "descendant::")) : _html.SelectNodes(xPath);
            }
            catch (Exception ex)
            {
                Log.Error("HtmlEngine.Select", ex.Message);
                return null;
            }
        }

        /// <summary>
        /// 获得XPath
        /// </summary>
        /// <param name="tag">标签</param>
        /// <param name="attr">属性</param>
        /// <param name="value">值</param>
        /// <returns>XPath</returns>
        protected string GetXPath(string tag, string attr, string value)
        {
            string xPath = "//" + tag;
            if (attr != null)
            {
                if (value != null)
                    xPath += "[@" + attr + "='" + value + "']";
                else
                    xPath += "[@" + attr + "]";
            }
            return xPath;
        }

        #endregion

        public void Dispose()
        {
            if (_html == null) return;
            _html.RemoveAll();
            _html = null;
        }
    }
}

